package com.game.timer;


import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TimerEvent<T> implements Runnable {
    public static final int INFINITE_CYCLE = 2147483647;

    public static int debugTime = 1000;

    private static final Logger log = LoggerFactory.getLogger(TimerEvent.class);

    protected TimerListener listener;

    protected T parameter;

    protected int intervalTime;

    protected TimeUnit timeUnit;

    protected int workId;

    protected int count;

    protected int initTime;

    protected boolean absolute;

    protected long startTime;

    protected long currentTime;

    protected long nextTime;

    protected volatile boolean inFire;

    protected volatile boolean enabled;

    protected TimerEvent(TimerListener listener, T parameter) {
        this.listener = listener;
        this.parameter = parameter;
    }

    public TimerEvent(TimerListener listener, int initTime, T parameter) {
        this(listener, parameter, 0, 1, initTime, false);
    }

    public TimerEvent(TimerListener listener, T parameter, int intervalTime, boolean absolute) {
        this(listener, parameter, intervalTime, 2147483647, 0, absolute);
    }

    public TimerEvent(TimerListener listener, T parameter, int intervalTime) {
        this(listener, parameter, intervalTime, 2147483647, 0, false);
    }

    public TimerEvent(TimerListener listener, T parameter, int intervalTime, int count) {
        this(listener, parameter, intervalTime, count, 0, false);
    }

    public TimerEvent(TimerListener listener, T parameter, int intervalTime, int count, boolean absolute) {
        this(listener, parameter, intervalTime, count, 0, absolute);
    }

    public TimerEvent(TimerListener listener, T parameter, int intervalTime, int count, int initTime) {
        this(listener, parameter, intervalTime, count, initTime, false);
    }

    public TimerEvent(TimerListener listener, long time, T parameter) {
        this.listener = listener;
        this.nextTime = time;
        this.parameter = parameter;
        this.count = 1;
    }

    public TimerEvent(TimerListener listener, T parameter, int intervalTime, int count, int initTime, boolean absolute) {
        this.listener = listener;
        this.parameter = parameter;
        this.intervalTime = intervalTime;
        this.count = count;
        this.initTime = initTime;
        this.absolute = absolute;
        this.inFire = false;
        this.enabled = true;
    }

    public TimerEvent withWork(int workId) {
        this.workId = workId;
        return this;
    }

    public TimerEvent withTimeUnit(TimeUnit timeUnit) {
        assert timeUnit != null;
        this.timeUnit = timeUnit;
        this.intervalTime = (int) timeUnit.toMillis(this.intervalTime);
        this.initTime = (int) timeUnit.toMillis(this.initTime);
        return this;
    }

    public TimerListener getTimerListener() {
        return this.listener;
    }

    public Object getParameter() {
        return this.parameter;
    }

    public TimerEvent setParameter(T parameter) {
        this.parameter = parameter;
        return this;
    }

    public int getIntervalTime() {
        return this.intervalTime;
    }

    public TimerEvent setIntervalTime(int time) {
        this.intervalTime = time;
        return this;
    }

    public int getCount() {
        return this.count;
    }

    public TimerEvent setCount(int count) {
        this.count = count;
        return this;
    }

    public TimerEvent setEnabled(boolean b) {
        this.enabled = b;
        return this;
    }

    public boolean getEnabled() {
        return this.enabled;
    }

    public int getInitTime() {
        return this.initTime;
    }

    public TimerEvent setInitTime(int initTime) {
        this.initTime = initTime;
        return this;
    }

    public boolean isAbsolute() {
        return this.absolute;
    }

    public TimerEvent setAbsolute(boolean b) {
        this.absolute = b;
        return this;
    }

    public long getStartTime() {
        return this.startTime;
    }

    public long getCurrentTime() {
        return this.currentTime;
    }

    public long getNextTime() {
        return this.nextTime;
    }

    public void setNextTime(long time) {
        this.nextTime = time;
    }

    public void init() {
        this.startTime = System.currentTimeMillis();
        this.enabled = true;
        if (this.nextTime == 0L)
            this.nextTime = this.startTime + this.initTime;
    }

    void fire(long currentTime) {
        if (this.count != Integer.MAX_VALUE)
            this.count--;
        this.currentTime = currentTime;
        this.nextTime = this.absolute ? (this.nextTime + this.intervalTime) : (currentTime + this.intervalTime);
        try {
            this.listener.onTimer(this);
        } catch (Throwable e) {
            if (log.isWarnEnabled())
                log.warn("fire error, " + toString(), e);
        }
        long endTime = System.currentTimeMillis();
        if (log.isDebugEnabled() &&
                endTime - currentTime > debugTime)
            log.warn("event fire long time,event=" + toString());
        this.inFire = false;
    }

    public String toString() {
        return super.toString() + "[listener=" + this.listener + ", parameter=" + this.parameter + ", intervalTime=" + this.intervalTime + ", count=" + this.count + ", initTime=" + this.initTime + ", absolute=" + this.absolute + "]";
    }

    public void run() {
        fire(System.currentTimeMillis());
    }
}